#include "MDSParser.h"
#include "xMappingTree.h"
#include "xMappingNode.h"
#include "xNodeType_VM.h"
#include "xNodeType_WOP.h"
#include "xNodeType_vmcollector.h"
#include "xNodeType_wopcollector.h"
#include "xNodeType_Math.h"
#include "xNodeType_vmclone.h"
#include "xNodeType_gate.h"
#include "xNodeType_composite.h"
#include "xNodeType_StringComposer.h"
#include "xNodeType_Clock.h"
#include "Factory_xNodeType.h"
#include <QString>
#include <assert.h>


MDSParser::MDSParser()
{

}

MDSParser::~MDSParser()
{

}

xMappingTree* MDSParser::fromMDStoXMappingTree( QString MDS, WOP_Entry* en )
{
	this->_en = en;
	QString headerString = "#MDS version 3.0";

	_xmt = new xMappingTree();

	map<string, xMappingNode*>::iterator kitr ;

	QStringList line = MDS.split("\n", QString::SkipEmptyParts);				// line 儲存MDS每一行資訊
	QStringList headerTokens = line.at(0).split(" ", QString::SkipEmptyParts);	// 把MDS第一行字串切token存在 headerTokens 裡
	QStringList bodyTokens;
	parseHeader(headerTokens);						// parse MDS header 部分

	for ( int i = 1; i < line.count(); ++i )
	{
		bodyTokens << line.at(i).split(" ", QString::SkipEmptyParts);
	}

	for ( int i = 0; i < bodyTokens.size(); ++i )
	{
		if (bodyTokens.at(i).compare("{") == 0|| 
			bodyTokens.at(i).compare("}") == 0)
		{
			bodyTokens.removeAt(i);
		}
	}

	QString body = bodyTokens.join(" ");			



	QStringList definitionBlock = getDefinitionBlock(body);
	QStringList::Iterator itr = definitionBlock.begin();

	while ( itr != definitionBlock.end() )
	{
		if ( (*itr).contains("node_definition")){
			parseNodeDefinition( *itr );
		}
		else if ( (*itr).contains("terminal_nodes")){
			parseTerminalNode( *itr );
		}
		else if ( (*itr).contains("link_definition")){
			parseLinkDefinition( *itr );
		}
		else if ( (*itr).contains("parameterized")){
			parseParameterized( *itr );
		}
		++itr;
	}

	_xmt->clearUnusedWOP();
	return _xmt ;
}

xMappingTree* MDSParser::fromMDStoXMappingTree( std::string stdMDS , WOP_Entry* en )
{
	QString mds(stdMDS.c_str());
	return fromMDStoXMappingTree(mds, en);
}

void MDSParser::parseHeader( QStringList &tokens )
{
	assert(tokens.at(0) == "#MDS");
	assert(tokens.at(1) == "version");
	assert(tokens.at(2) == MDS_VERSION);
}

void MDSParser::parseNodeDefinition( QString &mds )
{
	QStringList tokens = mds.split(" ");

	assert(tokens.at(0) == "node_definition");
	assert(tokens.at(1) == "(");
	removeItemFromTokens(tokens, 2);
	std::vector<std::string> stdTokens = toStdVector(tokens);
	

	// read all the node definition
	do {
		if( tokens.at( 0 ) == "{" ) {
			this->parseNestedNode( tokens, stdTokens );
		}

		string serialno = tokens.at(1).toStdString() ;
		string nodetype = tokens.at(2).toStdString() ; // nodetypes are "wop" "vm" "vmcollector"
		string parameter1 = tokens.at(3).toStdString(); // parameter 1 of an wop is vartype()
		string parameter2 = tokens.at(4).toStdString(); // parameter 2 of an wop is its shortname. 
		
		int usedTokens = addNode( nodetype, serialno, parameter1, parameter2, stdTokens);
		removeItemFromTokens(tokens, usedTokens);
		stdTokens = toStdVector(tokens);
	} while (tokens.at(0) != ")");
}

void MDSParser::parseNestedNode( QStringList & tokens, std::vector< std::string > & stdTokens ) {
	// remove "{"
	removeItemFromTokens( tokens, 1 );
	stdTokens = toStdVector( tokens );
	do {
		if( tokens.at( 0 ) == "{" ) {
			this->parseNestedNode( tokens, stdTokens );
		}

		string serialno = tokens.at(1).toStdString() ;
		string nodetype = tokens.at(2).toStdString() ; // nodetypes are "wop" "vm" "vmcollector"
		string parameter1 = tokens.at(3).toStdString(); // parameter 1 of an wop is vartype()
		string parameter2 = tokens.at(4).toStdString(); // parameter 2 of an wop is its shortname. 
		
		int usedTokens = addNode( nodetype, serialno, parameter1, parameter2, stdTokens );
		removeItemFromTokens( tokens, usedTokens );
		stdTokens = toStdVector( tokens );
	} while( tokens.at( 0 ) != "}" );
	// remove }
	removeItemFromTokens( tokens, 1 );
	stdTokens = toStdVector( tokens );
}

// parse mds 把 mds 變成一個一個的 block 
QStringList& MDSParser::getDefinitionBlock( QString body )
{
	QStringList *result = new QStringList();
	int offset = 0;

	while( !body.isEmpty() )
	{
		QStringList tokens = body.split( " ", QString::SkipEmptyParts );
		if (  tokens.at(0) == "node_definition" ||
			  tokens.at(0) == "terminal_nodes"  ||
			  tokens.at(0) == "link_definition" ||
			  tokens.at(0) == "parameterized"	||
			  tokens.at(0) == "output_terminals"
			)
		{
			assert(tokens.at(1)== "(");

			QString block;
			QStringList::iterator itr = tokens.begin();
			while ( *itr != ")" )
			{
				block.append(*itr);
				block.append(" ");
				offset = offset + (*itr).size() + 1;
				++itr;
			}
			block.append(")");
			++offset;

			result->push_back(block);

			body.remove(0, offset + 1); // offset+1 是因為要把空白也移除掉
			offset = 0;
		} else {
			assert(false);
		}
	}

	return *result;
}


void MDSParser::parseTerminalNode( QString &mds )
{
	QStringList tokens = mds.split(" ");
	std::vector<std::string> stdTokens = toStdVector(tokens);

	assert(tokens[0] == "terminal_nodes"); 
	assert(tokens[1] == "("); 

	unsigned int i = 2;

	while (tokens[i] != ")"){
		std::string serialno = stdTokens[i] ; // get the right mapping node
		addTerminalNode(serialno);
		i++ ;
	}
}

void MDSParser::parseLinkDefinition( QString &mds )
{
	QStringList tokens = mds.split(" ");
	std::vector<std::string> stdTokens = toStdVector(tokens);

	assert(tokens[0] == "link_definition");
	assert(tokens[1] == "(");
	removeItemFromTokens(tokens, 2);
	stdTokens = toStdVector(tokens);

	while (tokens[0] != ")") {
		addLink(tokens, stdTokens);
		removeItemFromTokens(tokens, 9);
		stdTokens = toStdVector(tokens);
	} ;
}

void MDSParser::parseParameterized( QString &mds )
{
	QStringList tokens = mds.split(QRegExp("\\s"), QString::SkipEmptyParts);

	assert(tokens[0] == "parameterized"); 
	assert(tokens[1] == "("); 
	
	QStringList parameterizedBlockString= tokens.mid(2);
	parseParameterizedBody(parameterizedBlockString);
	return;

}

void MDSParser::doForLoop( QString& statement , QString var ,int start, int end )
{

	QStringList tokens = statement.split(QRegExp("\\s"), QString::SkipEmptyParts);
	_varablesTable[var.toStdString()] = start;

	for ( int i = start; i <= end; ++i )
	{
		parseParameterizedBody(tokens);
		++_varablesTable[var.toStdString()];
	}
}

void MDSParser::addTerminalNode( std::string serialno )
{
	map<string, xMappingNode*>::iterator kitr ;
	kitr = _xnodes.find(serialno);
	assert(kitr != _xnodes.end());
	xMappingNode *terminal = kitr->second ;
	_xmt->add_input_terminal(terminal);
}

void MDSParser::parseParameterizedBody( QStringList tokens )
{

	unsigned int i = 0;

	while ( tokens[i] != ")" )
	{
		if ( (tokens[i].trimmed() == "FORLOOP") ){

			// FORLOOP 後面必須跟著一個"$"開頭的變數宣告
			assert(tokens[i+1].indexOf("$")==0);

			// 變數後面必須跟著兩個數字來表示for loop的範圍
			bool ok;
			int start = tokens[i+2].toInt(&ok);
			assert(ok);
			int end = tokens[i+3].toInt(&ok);
			assert(ok);

			_varablesTable[tokens[i+1].toStdString()]= start;

			QStringList loopBlockString = tokens.mid(i+4);
			loopBlockString.removeLast();
			doForLoop(loopBlockString.join(" "), tokens[i+1], start, end );
			i = i + 4 + loopBlockString.size();

		} 
		else if ( tokens[i] == "ADD_NODE")
		{
			tokens[i+1] = "node";
			
			assert ( tokens[i+2].indexOf("SN") == 0 );
			std::string serialno = generateSN();
			QString before = tokens[i+2];
			tokens.replaceInStrings(before, serialno.c_str());	// 把所有 SN 開頭的字串改成一個新的 serial number

			QStringList temp = tokens.mid(i+1);
			std::vector<std::string> stdTokens = toStdVector(temp);

			// 假如 varablesTable 不是空的table，表示有變數被宣告了，所以才做字串取代的動作，把變數取代成變數的值
			if ( !_varablesTable.empty()){
				replaceSymbolToValue(stdTokens);
				replaceSymbolToValue(tokens);
			}

			std::string nodetype = tokens.at(i+3).toStdString() ; // nodetypes are "wop" "vm" "vmcollector"
			std::string parameter1 = tokens.at(i+4).toStdString(); // parameter 1 of an wop is vartype()
			std::string parameter2 = tokens.at(i+5).toStdString(); // parameter 2 of an wop is its shortname. 

			int usedTokens = addNode(nodetype, serialno, parameter1, parameter2, stdTokens);
			i = i + 1 + usedTokens;

		}
		else if ( tokens[i] == "ADD_LINK")
		{
			QStringList temp = tokens.mid(i);
			removeItemFromTokens(temp, 1);
			std::vector<std::string> stdTokens = toStdVector(temp);
			addLink(temp,stdTokens);
			i = i + 10;
		}
		else if ( tokens[i] == "ADD_TERMINAL")
		{
			std::string serialno = tokens[i+1].toStdString();
			addTerminalNode(serialno);
			i = i + 2;
		}
		else if ( tokens[i] == "ENDLOOP" )
		{
			return;
		}
	}
}

int MDSParser::addNode( std::string nodetype, std::string serialno, std::string parameter1, std::string parameter2, std::vector<std::string> &stdTokens )
{

	unsigned int usedtokens = 0;
	

	if (nodetype == "wop") {
		assert(_en != NULL);
		// now, we search all the children of wop en and return the matched one
		WOP_Entry *wop = _en->findMatchedWOP(parameter1, parameter2) ; 
		// where parameter1 is vartype, parameter2 is shortname of wop entry
		assert(wop != NULL);
		xNodeType *wp = new xNodeType_WOP(wop);
		xMappingNode *wnode = _xmt->create_node(wp);

		usedtokens = wp->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = wnode ;

	} 

	else if (nodetype == "vm") {
		istringstream buf(parameter1);
		string idname ;
		buf >> idname ;

		istringstream buf2(parameter2);
		int inp_no ;
		buf2 >> inp_no ;

		xNodeType_VM *vm = new xNodeType_VM(idname);
		xMappingNode *vmnode = _xmt->create_node((xNodeType*)vm); // the default string begins at i+5
		usedtokens = vm->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = vmnode ;
	} 

	else if (nodetype == "vmcollector") {
		//xNodeType *vmcollector_nt = (xNodeType *) new xNodeType_vmcollector();
		xNodeType *vmcollector_nt = new xNodeType_vmcollector();
		xMappingNode *vmcollector = _xmt->create_node(vmcollector_nt);
		usedtokens = vmcollector_nt->parseMDString(stdTokens,0);

		_xnodes[serialno] = vmcollector ;
	} 

	else if (nodetype == "wopcollector") {
		//xNodeType *wopcollector_nt = (xNodeType *) new xNodeType_wopcollector();
		xNodeType *wopcollector_nt = new xNodeType_wopcollector();
		xMappingNode *wopcollector = _xmt->create_node(wopcollector_nt);
		usedtokens = wopcollector_nt->parseMDString(stdTokens,0);

		_xnodes[serialno] = wopcollector ;
	} 

	else if (nodetype == "math") {
		xNodeType_Math *am = new xNodeType_Math();
		xNodeType *nt = am ;
		xMappingNode *amnode = _xmt->create_node(nt);
		usedtokens = am->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = amnode ;
	} 

	else if (nodetype == "stringcomposer"){
		xNodeType_StringComposer *scm = new xNodeType_StringComposer();
		xNodeType *nt = scm;
		xMappingNode *amnode = _xmt->create_node(nt);
		usedtokens = scm->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = amnode ;
	} 

	else if(nodetype == "clock"){
		xNodeType_Clock *cm = new xNodeType_Clock();
		xNodeType *nt = cm;
		xMappingNode *amnode = _xmt->create_node(nt);
		usedtokens = cm->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = amnode ;
	} 

	else if (nodetype == "gate") {
		xNodeType *gate = new xNodeType_gate(parameter1);
		xMappingNode *amnode = _xmt->create_node(gate);
		usedtokens = gate->parseMDString(stdTokens,0);

		_xnodes[serialno] = amnode ;
	} 

	else if (nodetype == "vmclone") {
		xNodeType *vmclone_nt = new xNodeType_vmclone();
		xMappingNode *amnode = _xmt->create_node(vmclone_nt);
		usedtokens = vmclone_nt->parseMDString(stdTokens,0);

		_xnodes[serialno] = amnode ;
	} 

	else if (nodetype == "composite") {
		xNodeType *composite_nt = new xNodeType_composite(parameter1);
		xMappingNode *cnode = _xmt->create_node(composite_nt);
		usedtokens = composite_nt->parseMDString(stdTokens,0) ;

		_xnodes[serialno] = cnode ;
	} 

	else 
		assert(false);

	
	return usedtokens;
}

void MDSParser::removeItemFromTokens( QStringList& tokens, int length )
{
	for ( int i = 0; i < length; ++i )
	{
		tokens.removeFirst();
	}
}

std::vector<std::string> MDSParser::toStdVector( QStringList& tokens )
{
	std::vector<std::string> stdTokens;

	QStringList::Iterator itr = tokens.begin();

	while ( itr != tokens.end() )
	{
		stdTokens.push_back((*itr).toStdString());
		++itr;
	}

	return stdTokens;
}

std::string MDSParser::generateSN()
{
	std::map<std::string, xMappingNode *>::iterator itr;
	int maxValue = 0;

	for ( itr = _xnodes.begin(); itr != _xnodes.end(); ++itr )
	{
		int sn = atoi(itr->first.c_str());
		maxValue = qMax(maxValue, sn);
	}

	// int to std::string
	std::string sn;
	std::stringstream st;
	st << maxValue+1;
	sn = st.str();

	return sn;
}

void MDSParser::replaceSymbolToValue( std::vector<std::string> &stdTokens )
{
	QStringList tokens;
	std::vector<std::string>::iterator sitr;
	for ( sitr = stdTokens.begin(); sitr != stdTokens.end(); ++sitr )
	{
		tokens << (*sitr).c_str();
	}

	replaceSymbolToValue(tokens);
	stdTokens = toStdVector(tokens);

}

void MDSParser::replaceSymbolToValue( QStringList &tokens )
{
	std::map<std::string, int>::iterator itr;
	for ( itr = _varablesTable.begin(); itr != _varablesTable.end(); ++itr )
	{
		tokens.replaceInStrings( QString(itr->first.c_str()), QString::number(itr->second) );
	}
}

void MDSParser::addLink( QStringList tokens, std::vector<std::string> stdTokens )
{

	assert(tokens[0] == "node") ;

	map<string, xMappingNode*>::iterator kitr ;
	string leftserialno = stdTokens[1] ;	// get the left mapping node
	kitr = _xnodes.find(leftserialno);
	assert(kitr != _xnodes.end());
	xMappingNode *left = kitr->second ;

	assert(tokens[2] == "port");
	istringstream obuf(stdTokens[3]);
	int outportid ;
	obuf >> outportid ;

	assert(tokens[4] == "to");
	assert(tokens[5] == "node");

	string rightserialno = stdTokens[6] ; // get the right mapping node
	kitr = _xnodes.find(rightserialno);
	assert(kitr != _xnodes.end());
	xMappingNode *right = kitr->second ;



	// some special cases like vmcollector and wopcollector which have dynamic number of inports
	// need to be handled specially

	if (right->getNodeTypePtr()->getType() == "vm_collector") {
		xInMappingPort *newinp = right->newInPort("vmport","vm", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
		_xmt->create_link(left,outportid, newinp);
		return;
	} else if (right->getNodeTypePtr()->getType() == "wopcollector"||
		right->getNodeTypePtr()->getType() == "math") {
		xInMappingPort *newinp = right->newInPort("wopport","wop", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
		_xmt->create_link(left,outportid, newinp);
		return;
	} else if (right->getNodeTypePtr()->getType() == "string_composer") {
		xInMappingPort *newinp = right->newInPort("wopport","tostring*", CLASS_REQUIRED, "NULL", CATEGORY_OTHER);
		_xmt->create_link(left,outportid, newinp);
		return;
	}
	else {

		assert(tokens[7] == "port");  
		istringstream buf(stdTokens[8]);// get the port index 
		int portid ;
		buf >> portid ;

		_xmt->create_link(left,outportid, right->getInPort(portid));
	}
}
